/**
 * Base Validator Class
 * Provides common validation functionality for all configuration validators
 */

class BaseValidator {
  /**
   * Create a new validator instance
   * @param {string} name - Validator name for identification
   * @param {Object} options - Validation options
   * @param {boolean} options.strict - Whether to use strict validation
   * @param {boolean} options.testMode - Whether to use test-friendly validation
   */
  constructor(name, options = {}) {
    this.name = name;
    this.options = {
      strict: false,
      testMode: false,
      allowExtraFields: true,
      ...options
    };
  }

  /**
   * Validate data against validator rules
   * @param {*} data - Data to validate
   * @param {Object} context - Validation context
   * @returns {Object} Validation result
   */
  validate(data, context = {}) {
    if (!this.shouldValidate(data)) {
      return this.success(data);
    }

    const result = this.performValidation(data, context);

    if (this.options.testMode) {
      return this.adaptForTest(result);
    }

    return result;
  }

  /**
   * Determine if validation should be performed
   * @param {*} data - Data to check
   * @returns {boolean} Whether to validate
   */
  shouldValidate(data) {
    return data !== null && data !== undefined;
  }

  /**
   * Perform actual validation - to be implemented by subclasses
   * @param {*} data - Data to validate
   * @param {Object} context - Validation context
   * @returns {Object} Validation result
   */
  performValidation(data, context) {
    throw new Error(`performValidation must be implemented by ${this.constructor.name}`);
  }

  /**
   * Adapt validation result for test environment
   * @param {Object} result - Original validation result
   * @returns {Object} Test-friendly result
   */
  adaptForTest(result) {
    if (result.success) {
      return {
        ...result,
        testMode: true
      };
    }

    // In test mode, provide more helpful error messages
    const adaptedErrors = result.errors.map(error => ({
      field: error.field || 'unknown',
      message: error.message,
      value: error.value,
      suggestion: this.getSuggestion(error)
    }));

    return {
      ...result,
      errors: adaptedErrors,
      testMode: true
    };
  }

  /**
   * Get suggestion for fixing validation error
   * @param {Object} error - Validation error
   * @returns {string} Suggestion message
   */
  getSuggestion(error) {
    return `Check the ${error.field} field for correct format and value.`;
  }

  /**
   * Create a successful validation result
   * @param {*} data - Validated data
   * @returns {Object} Success result
   */
  success(data) {
    return {
      success: true,
      data: data,
      validator: this.name
    };
  }

  /**
   * Create a failed validation result
   * @param {string} message - Error message
   * @param {Array} errors - Array of error details
   * @returns {Object} Failure result
   */
  failure(message, errors = []) {
    return {
      success: false,
      error: message,
      errors: Array.isArray(errors) ? errors : [errors],
      validator: this.name
    };
  }

  /**
   * Create a test-friendly configuration
   * @param {Object} overrides - Configuration overrides
   * @returns {Object} Test configuration
   */
  createTestConfig(overrides = {}) {
    throw new Error(`createTestConfig must be implemented by ${this.constructor.name}`);
  }

  /**
   * Validate configuration for testing purposes
   * @param {Object} config - Configuration to validate
   * @returns {Object} Test validation result
   */
  validateForTest(config) {
    const originalOptions = { ...this.options };
    this.options.testMode = true;
    this.options.strict = false;
    this.options.allowExtraFields = true;

    const result = this.validate(config);

    this.options = originalOptions;
    return result;
  }
}

module.exports = BaseValidator;